package com.gamebase.net.rpc
{
	import com.gamebase.net.rpc.baseTypeEx.int16;
	import com.gamebase.net.rpc.baseTypeEx.int8;
	import com.gamebase.net.rpc.baseTypeEx.uint16;
	import com.gamebase.net.rpc.baseTypeEx.uint8;
	
	import flash.errors.EOFError;
	import flash.utils.ByteArray;
	import flash.utils.Dictionary;
	import flash.utils.Endian;
	import flash.utils.describeType;
	import flash.utils.getDefinitionByName;
	import flash.utils.getQualifiedClassName;
	
	public class Serializer
	{
		private var _hasError:Boolean;
		private var buffer:ByteArray;
		private var bytes:ByteArray;
		private var cassDic:Dictionary;
		private static var timezoneOffset:Number=(new Date()).timezoneOffset * 60000;
		
		
		public function Serializer(ba:ByteArray)
		{
			bytes=new ByteArray();
			bytes.endian=Endian.BIG_ENDIAN;
			buffer=ba;
			buffer.endian=Endian.BIG_ENDIAN;
			cassDic = new Dictionary();
		}
		public function reset():void
		{
			buffer.position = 0;
			
		}
		
		public function getBuffer():ByteArray
		{
			return buffer;
		}
		
		public function setBuffer(ba:ByteArray):void
		{
			buffer=ba;
			buffer.endian=Endian.BIG_ENDIAN;
			_hasError=false;
		}
		
		public function get hasError():Boolean
		{
			return _hasError;
		}
		
		public function set hasError(value:Boolean):void
		{
			_hasError=value;
		}
		
		public function readObject(obj:ISerializable):ISerializable
		{
			obj.unserialize(this);
			return obj;
		}
		
		public function writeObject(obj:ISerializable):void
		{
			obj.serialize(this);
		}
		
		public function readBoolean():Boolean
		{
			if (_hasError)
				return false;
			
			if (buffer.bytesAvailable >= 1)
			{
				return buffer.readBoolean();
			}
			else
			{
				_hasError=true;
				Debug.append("readBoolean,序列化错误");
				return false;
			}
		}
		
		public function writeBoolean(value:Boolean):void
		{
			buffer.writeBoolean(value);
		}
		
		public function readByte():int
		{
			if (_hasError)
				return 0;
			
			if (buffer.bytesAvailable >= 1)
			{
				return buffer.readByte();
			}
			else
			{
				_hasError=true;
				Debug.append("readByte,序列化错误");
				return 0;
			}
		}
		
		public function writeByte(value:int):void
		{
			buffer.writeByte(value);
		}
		
		public function readUnsignedByte():uint
		{
			if (_hasError)
				return 0;
			
			if (buffer.bytesAvailable >= 1)
			{
				return buffer.readUnsignedByte();
			}
			else
			{
				_hasError=true;
				Debug.append("readUnsignedByte,序列化错误");
				return 0;
			}
		}
		
		public function writeUnsignedByte(value:uint):void
		{
			bytes.position=0;
			bytes.writeUnsignedInt(value);
			buffer.writeBytes(bytes, 0, 1);
		}
		
		public function readShort():int
		{
			if (_hasError)
				return 0;
			
			if (buffer.bytesAvailable >= 2)
			{
				return buffer.readShort();
			}
			else
			{
				_hasError=true;
				Debug.append("readShort,序列化错误");
				return 0;
			}
		}
		
		public function writeShort(value:int):void
		{
			buffer.writeShort(value);
		}
		
		public function readUnsignedShort():uint
		{
			if (_hasError)
				return 0;
			
			if (buffer.bytesAvailable >= 2)
			{
				return buffer.readUnsignedShort();
			}
			else
			{
				_hasError=true;
				Debug.append("readUnsignedShort,序列化错误");
				return 0;
			}
		}
		
		public function writeUnsignedShort(value:uint):void
		{
			bytes.position=0;
			bytes.writeShort(value);
			buffer.writeBytes(bytes, 0, 2);
		}
		
		public function readInt():int
		{
			if (_hasError)
				return 0;
			
			if (buffer.bytesAvailable >= 4)
			{
				return buffer.readInt();
			}
			else
			{
				_hasError=true;
				Debug.append("readInt,序列化错误");
				return 0;
			}
		}
		
		public function writeInt(value:int):void
		{
			buffer.writeInt(value);
		}
		
		public function readUnsignedInt():uint
		{
			if (_hasError)
				return 0;
			
			if (buffer.bytesAvailable >= 4)
			{
				return buffer.readUnsignedInt();
			}
			else
			{
				_hasError=true;
				Debug.append("readUnsignedInt,序列化错误");
				return 0;
			}
		}
		
		public function readUTF():String
		{
			return buffer.readUTF();
		}
		
		public function writeUnsignedInt(value:uint):void
		{
			buffer.writeUnsignedInt(value);
		}
		
		public function readSingle():Number
		{
			if (_hasError)
				return 0;
			
			if (buffer.bytesAvailable >= 4)
			{
				return buffer.readFloat();
			}
			else
			{
				_hasError=true;
				Debug.append("readSingle,序列化错误");
				return 0;
			}
		}
		
		public function writeSingle(value:Number):void
		{
			buffer.writeFloat(value);
		}
		
		public function readDouble():Number
		{
			if (_hasError)
				return 0;
			
			if (buffer.bytesAvailable >= 8)
			{
				return buffer.readDouble();
			}
			else
			{
				_hasError=true;
				Debug.append("readDouble,序列化错误");
				return 0;
			}
		}
		
		public function writeDouble(value:Number):void
		{
			buffer.writeDouble(value);
		}
		
		public function readString():String
		{
			if (_hasError)
				return null;
			
			try
			{
				return buffer.readUTF();
			}
			catch(e:EOFError)
			{
				_hasError=true;
				Debug.append("readString,序列化错误");
			}
			
			return null;
		}
		
		public function writeString(value:String):void
		{
			if (value != null)
				buffer.writeUTF(value);
			else
				buffer.writeUTF("");
		}
		
		public function readUTFBytes(length:uint):String
		{
			if (_hasError)
				return null;
			
			if (buffer.bytesAvailable >= length)
			{
				return buffer.readUTFBytes(length);
			}
			else
			{
				_hasError=true;
				Debug.append("readUTFBytes,序列化错误");
				return null;
			}
		}
		
		public function writeUTFBytes(value:String):void
		{
			buffer.writeUTFBytes(value);
		}
		
		public function readVarLenGuid():Guid
		{
			var g:Guid=new Guid();
			var len:uint=buffer.readByte();
			g.setBytes(buffer, len);
			return g;
		}
		
		public function writeVarLenGuid(value:Guid, length:uint):void
		{
			buffer.writeByte(length);
			value.getBytes(buffer, length);
		}
		
		public function readGuid():Guid
		{
			var g:Guid=new Guid();
			g.setBytes(buffer, 16);
			return g;
		}
		
		public function writeGuid(value:Guid):void
		{
			value.getBytes(buffer, 16);
		}
		
		public function readDateTime():Date
		{
			if (_hasError)
				return null;
			var dt:Date=new Date();
			dt.setTime(readUnsignedInt()*1000);
			return dt;
		}
		
		public function writeDateTime(value:Date):void
		{
			if (value != null)
				writeUnsignedInt(uint(value.getTime()*0.0001));
			else
				writeUnsignedInt(0);
		}
		
		public function readBytes():ByteArray
		{
			if (_hasError)
				return null;
			
			var len:uint=0;
			var bytes:ByteArray=null;
			
			len=buffer.readUnsignedInt();
			if (len > 0)
			{
				if (buffer.bytesAvailable >= len)
				{
					buffer.readBytes(bytes, 0, len);
				}
				else
				{
					_hasError=true;
					Debug.append("readBytes,序列化错误");
				}
			}
			
			return bytes;
		}
		
		public function writeBytes(value:ByteArray):void
		{
			buffer.writeUnsignedInt(value.bytesAvailable);
			buffer.writeBytes(value);
		}
		
		public function readByteArray(length:uint):ByteArray
		{
			if (_hasError)
				return null;
			
			var bytes:ByteArray=null;
			
			if (buffer.bytesAvailable >= length)			
				buffer.readBytes(bytes, 0, length);
			
			else
			{
				_hasError=true;
				Debug.append("readByteArray,序列化错误");
			}
			
			return bytes;
		}
		
		public function writeByteArray(value:ByteArray):void
		{
			buffer.writeBytes(value);
		}
		
		//-----------------------序列化规则 ----------------
		public function  encodeData($data:Object, toByteArray:ByteArray):void
		{
			if($data == null) 
				return;
			
			if($data is int8)
			{
				toByteArray.writeByte(int($data.value));
			}
			else if($data is uint8)
			{
				toByteArray.writeUTFBytes(String($data.value));
			}
			else if(($data is int16) || ($data is uint16))
			{
				toByteArray.writeShort(int($data.value));
			}
			else if($data is int)
			{
				toByteArray.writeInt(int($data));
			}
			else if($data is uint)
			{
				toByteArray.writeUnsignedInt(uint($data));
			}
			else if($data is Number)
			{
				toByteArray.writeDouble(Number($data));
			}
			else if($data is String)
			{
				toByteArray.writeUTF(String($data));
			}
			else if($data is XML)
			{
				toByteArray.writeUTF($data.toXMLString());
			}
			else if($data is Boolean)
			{
				toByteArray.writeBoolean(Boolean($data));
			}
			else if(($data is Vector) || ($data is Array))
			{
				var j:uint = 0;
				encodeData(new int16($data.length), toByteArray);//集合类型的数据先序列化集合的长度
				while(j< $data.length)
				{
					encodeData($data[j], toByteArray);
					j++;
				}
			}
			else if($data is Object /*ISerializable*/)
			{
				var attTypeArr:Array = getAttNameType(flash.utils.describeType($data), true);
				var i:uint = 0;
				while(i<attTypeArr.length)
				{
					encodeData($data[attTypeArr[i][0]], toByteArray);
					i++;
				}
			}
			else
			{
				throw new Error("遇到未设定序列化类型:"+$data);
			}
			
		}
		
		public function decodeData(targetClass:Object, formByteArray:ByteArray):Object
		{
			if(formByteArray.bytesAvailable == 0)
				return null;
			var obj:Object = new targetClass();
			if(obj is int8)
			{
				obj.value = formByteArray.readByte();
				return obj;
			}
			else if(obj is uint8)
			{
				obj.value = formByteArray.readUnsignedByte();
				return obj;
			}
			else if(obj is int16)
			{
				obj.value = formByteArray.readShort();
				return obj;
			}
			else if(obj is uint16)
			{
				obj.value = formByteArray.readUnsignedShort();
				return obj;
			}
			else if(obj is int)
			{
				obj = formByteArray.readInt();
				return obj;
			}
			else if(obj is uint)
			{
				obj = formByteArray.readUnsignedInt();
				return obj;
			}
			else if(obj is Number)
			{
				obj = formByteArray.readDouble();
				return obj;
			}
			else if(obj is String)
			{
				var l:int  = formByteArray.readShort();
				obj = formByteArray.readUTFBytes(l);
				//obj = formByteArray.readUTF();
				return obj;
			}
			else if(obj is XML)
			{
				obj = XML(formByteArray.readUTF());
				return obj;
			}
			else if(obj is Boolean)
			{
				obj = formByteArray.readBoolean();
				return obj;
			}
			else if(obj is Array)
			{
				throw new Error("S-C数据类型不能为Array，请用Vector:"+targetClass);
			}
			else if(obj is Object /*ISerializable*/)
			{
				var descriptXML:XML = flash.utils.describeType(targetClass);
				if(descriptXML.factory[0].@type[0].indexOf("__AS3__.vec::Vector") == 0)
				{
					var n:uint = 0;
					var j:uint = formByteArray.readUnsignedShort();//集合类型的数据先读取集合的长度
					//var classXML:XML = flash.utils.describeType(targetClass);
					var classString:String = descriptXML.factory[0].attribute("type")[0].toString();
					classString = classString.replace("__AS3__.vec::Vector.<","");
					classString = classString.substr(0, classString.length-1);
					var elementClass:Object = cassDic[classString];
					if(elementClass == null)
					{
						elementClass = flash.utils.getDefinitionByName(classString);
						cassDic[classString] = elementClass;
					}
						
					while(n < j)
					{
						obj.push(decodeData(elementClass, formByteArray));
						n++;
					}
				}
				else
				{
					var attTypeArr:Array = getAttNameType(descriptXML, false);
					var i:uint = 0;
					var decodeClass:Object;
					while(i<attTypeArr.length)
					{
						decodeClass = cassDic[attTypeArr[i][1]];
						if(decodeClass == null)
						{
							decodeClass = flash.utils.getDefinitionByName(attTypeArr[i][1]);
							cassDic[attTypeArr[i][1]] = decodeClass;
						}
						obj[attTypeArr[i][0]] = decodeData(decodeClass, formByteArray);
						i++;
					}
				}
			}
			else
			{
				throw new Error("遇到未设定序列化类型:"+flash.utils.getQualifiedClassName(targetClass));
			}
			
			return obj;
			
		}
		
		private function getAttNameType($s:XML, isInstance:Boolean):Array
		{
			var a:Array = [];
			var attPare:Array = [];
			var attList:XMLList;
			if(isInstance)
				attList = $s.variable;
			else
				attList = $s.factory[0].variable;
			for each(var variable:XML in attList)
			{
				
				attPare.push({"pos":int(variable.metadata[0].arg[0].@value[0]), "att":[String(variable.@name[0]), String(variable.@type[0])]});
			}
			attPare.sortOn("pos", Array.NUMERIC);  
			var i:uint = 0;
			while(i<attPare.length)
			{
				//trace(attPare[i].pos);
				a.push(attPare[i].att);
				
				i++; 
			}
			//trace(a);
			return a;
		}
		
	}
}